----------The Kingdom of Facts---------
A 4am crack                  2017-12-01
---------------------------------------

Name: The Kingdom of Facts
Genre: educational
Year: 1984
Credits: Sheilla Morrell (graphics),
  Anders Beitnes (programming), Rita
  and Larry Levin, Wendy Barels, Cindy
  Taylor (questions), Santa Barbara
  Softworks
Publisher: Adventure International
Platform: Apple ][+ or later
Media: double-sided 5.25-inch disk
OS: DOS 3.3
Previous cracks: none

                   ~

               Chapter 0
 In Which Various Automated Tools Fail
          In Interesting Ways


COPYA
  read error on last pass

Locksmith Fast Disk Backup
  unable to read track $22
  disk boots and game loads up to the
  initial play screen, but does not
  respond to user input thereafter
  (game is unplayable)

EDD 4 bit copy (no sync, no count)
  ditto

Copy ][+ nibble editor
  track $22 is weeeeeeeird, mostly #$FF
  and #$DD nibbles, like hundreds and
  hundreds of them in a row, and timing
  bits between each nibble

Disk Fixer
  bootloader is standard DOS 3.3
  RWTS is standard
  T01,S09 -> startup program is "HELLO"
  no way to read track $22 (no standard
  structure)

Why didn't any of my copies work?
  probably a runtime check on that
  weird track $22

Next steps:

  1. Find the runtime protection check
  2. Disable it
  3. Declare victory (*)

(*) go to the gym

                   ~

               Chapter 1
 In Which We Quickly Get To The Point


Booting my non-working copy, I press
<Ctrl-C> and drop to a prompt with DOS
in memory.

]LIST
;
; some interesting options (press "E"
; at the title screen to access the
; game editor, or "D" to initialize a
; data disk) but otherwise harmless
;
 170  PRINT : PRINT  CHR$ (4);"BR
     UN GAME"

]BLOAD GAME
]CALL -151

*AA72.AA73      ; address of last BLOAD

AA72- 00 20

*2000L

; ensure DOS I/O vectors are hooked up
2000-   20 51 A8    JSR   $A851

; "print" a DOS command to execute it
2003-   A0 00       LDY   #$00
2005-   B9 3C 20    LDA   $203C,Y
2008-   F0 06       BEQ   $2010
200A-   20 ED FD    JSR   $FDED
200D-   C8          INY
200E-   D0 F5       BNE   $2005

*FC58G N 400<203C.204FM

MDBLOADFACTSM@

This is equivalent to the BASIC command

PRINT CHR$(13);CHR$(4);"BLOADFACTS"

or simply typing "BLOAD FACTS" at the
prompt.

Let's do that.

*BLOAD FACTS

*AA72.AA73      ; address of last BLOAD

AA72- 00 40

Continuing from $2010...

2010-   2C 20 2C    BIT   $2C20

; weird, we're modifying code at $204A
; then immediately calling it
2013-   A9 EA       LDA   #$EA
2015-   8D 4A 20    STA   $204A
2018-   20 4A 20    JSR   $204A

; set 6 other addresses (these are all
; within the range of the "FACTS" file
; we just BLOADed)
201B-   A9 4A       LDA   #$4A
201D-   8D 94 5C    STA   $5C94
2020-   A9 0A       LDA   #$0A
2022-   8D 9D 5C    STA   $5C9D
2025-   A9 AA       LDA   #$AA
2027-   8D 20 5D    STA   $5D20
202A-   A9 60       LDA   #$60
202C-   8D 9C 5B    STA   $5B9C
202F-   A9 30       LDA   #$30
2031-   8D DE 5B    STA   $5BDE
2034-   A9 05       LDA   #$05
2036-   8D DF 5B    STA   $5BDF

; jump to $4000 to start the game
2039-   4C 00 40    JMP   $4000

*204AL

204A-   60          RTS

But wait! We just modified this (at
$2015). $EA is the NOP opcode; the
caller made this fall through to the
next instruction.

; counter?
204B-   A9 02       LDA   #$02
204D-   8D 54 20    STA   $2054

; unconditional branch
2050-   D0 03       BNE   $2055
...

; more self-modifying code, now an RTS
2055-   A9 60       LDA   #$60
2057-   8D 55 20    STA   $2055

; get address of RWTS parameter table
; (aha!)
205A-   20 E3 03    JSR   $03E3
205D-   84 F8       STY   $F8
205F-   85 F9       STA   $F9

; save last-used slot
2061-   A0 0F       LDY   #$0F
2063-   B1 F8       LDA   ($F8),Y
2065-   8D 52 20    STA   $2052

; RWTS command 0 = seek
2068-   A9 00       LDA   #$00
206A-   A0 0C       LDY   #$0C
206C-   91 F8       STA   ($F8),Y

; set track to seek
206E-   A0 04       LDY   #$04
2070-   AD 53 20    LDA   $2053
2073-   91 F8       STA   ($F8),Y

*2053

2053- 22

Aha! Track $22, the unreadable track.
This is definitely the copy protection.

; call the RWTS to execute the seek
2075-   20 E3 03    JSR   $03E3
2078-   20 D9 03    JSR   $03D9

; recalibrate the drive head by
; claiming to be on track $28 and
; seeking back to track 0
207B-   AE 52 20    LDX   $2052
207E-   A9 50       LDA   #$50
2080-   8D 78 04    STA   $0478
2083-   A9 00       LDA   #$00
2085-   20 A0 B9    JSR   $B9A0

; then back to track $22
2088-   AE 52 20    LDX   $2052
208B-   A9 00       LDA   #$00
208D-   8D 78 04    STA   $0478
2090-   AD 53 20    LDA   $2053
2093-   0A          ASL
2094-   20 A0 B9    JSR   $B9A0

We're definitely on track $22 now, and
it's definitely the unreadable track.

; these are used as raw disk nibbles
2097-   A9 DD       LDA   #$DD
2099-   85 F9       STA   $F9
209B-   A9 FF       LDA   #$FF
209D-   85 F8       STA   $F8

; turn on drive motor manually
209F-   AE 52 20    LDX   $2052
20A2-   BD 89 C0    LDA   $C089,X

; initialize counters
20A5-   A9 00       LDA   #$00
20A7-   85 F0       STA   $F0
20A9-   85 F1       STA   $F1
20AB-   85 F2       STA   $F2
20AD-   85 F3       STA   $F3
20AF-   85 F4       STA   $F4
20B1-   85 F5       STA   $F5
20B3-   85 F6       STA   $F6
20B5-   85 F7       STA   $F7
20B7-   A8          TAY
20B8-   AE 52 20    LDX   $2052

; look for #$DD or #$FF nibble
20BB-   BD 8E C0    LDA   $C08E,X
20BE-   BD 8C C0    LDA   $C08C,X
20C1-   10 FB       BPL   $20BE
20C3-   C5 F8       CMP   $F8    ; #$FF
20C5-   F0 62       BEQ   $2129
20C7-   C5 F9       CMP   $F9    ; #$DD
20C9-   F0 05       BEQ   $20D0
20CB-   C8          INY
20CC-   D0 F0       BNE   $20BE
20CE-   F0 6E       BEQ   $213E

There are three ways out of this loop.
Two of them are OK -- we find an #$FF
nibble and branch to $2129, or we find
a #$DD nibble and branch to $20D0. Or
we don't find either of those nibbles
within 256 tries, and we give up and
branch to $213E.

If we find a #$DD nibble first, we
continue to $20D0:

; look for #$FF nibble
20D0-   BD 8C C0    LDA   $C08C,X
20D3-   10 FB       BPL   $20D0

; branch forward once we find it
20D5-   C5 F8       CMP   $F8    ; #$FF
20D7-   F0 0E       BEQ   $20E7

; count the non-#$FF nibbles
20D9-   E6 F6       INC   $F6
20DB-   D0 F3       BNE   $20D0
20DD-   E6 F7       INC   $F7
20DF-   A5 F7       LDA   $F7

; give up if we've seen $1000 nibbles
; without finding #$FF
20E1-   C9 10       CMP   #$10
20E3-   90 EB       BCC   $20D0
20E5-   B0 57       BCS   $213E

; success path continues here (from the
; BEQ at $20D7) --
; now look for non-#$FF nibble
20E7-   BD 8C C0    LDA   $C08C,X
20EA-   10 FB       BPL   $20E7
20EC-   C5 F8       CMP   $F8    ; #$FF
20EE-   D0 0E       BNE   $20FE

; and give up after $1000 wrong nibbles
20F0-   E6 F0       INC   $F0
20F2-   D0 F3       BNE   $20E7
20F4-   E6 F1       INC   $F1
20F6-   A5 F1       LDA   $F1
20F8-   C9 10       CMP   #$10
20FA-   90 EB       BCC   $20E7
20FC-   B0 40       BCS   $213E

; if the first non-#$FF nibble is #$DD,
; that's good
20FE-   C5 F9       CMP   $F9    ; #$DD
2100-   F0 06       BEQ   $2108

; otherwise try again (but eventually
; give up after $100 non-#$DD nibbles)
2102-   E6 F4       INC   $F4
2104-   D0 E1       BNE   $20E7
2106-   F0 36       BEQ   $213E

; execution continues here (from "BEQ"
; at $2100) --
; count #$DD nibbles (zero page $F2/3)
; but give up after $1000 of them,
; terminate on #$FF nibble, tolerate up
; to 256 other values (zero page $F5)
2108-   BD 8C C0    LDA   $C08C,X
210B-   10 FB       BPL   $2108
210D-   C5 F9       CMP   $F9    ; #$DD
210F-   F0 0A       BEQ   $211B
2111-   C5 F8       CMP   $F8    ; #$FF
2113-   F0 6E       BEQ   $2183
2115-   E6 F5       INC   $F5
2117-   D0 EF       BNE   $2108
2119-   F0 23       BEQ   $213E
211B-   E6 F2       INC   $F2
211D-   D0 E9       BNE   $2108
211F-   E6 F3       INC   $F3
2121-   A5 F3       LDA   $F3
2123-   C9 10       CMP   #$10
2125-   90 E1       BCC   $2108
2127-   B0 15       BCS   $213E

This path continues at $2183. This next
line is the start of the other major
path, from the "BEQ" at $20C5, if we
initially found an #$FF nibble instead
of #$DD. (Tracks are just endless
circles, and there's no way to know
where in the circle we'll start. Rather
than burn through half the track
looking for one or the other, this
protection check is optimized to start
counting either way.)

                   ~

               Chapter 2
   In Which We Enter The Upside Down


This path is just the inverse of the
other path. Everything that was #$FF is
now #$DD, and vice versa.

; look for #$DD nibble
2129-   BD 8C C0    LDA   $C08C,X
212C-   10 FB       BPL   $2129
212E-   C5 F9       CMP   $F9    ; #$DD
2130-   F0 0F       BEQ   $2141

; count the non-#$DD nibbles
2132-   E6 F6       INC   $F6
2134-   D0 F3       BNE   $2129
2136-   E6 F7       INC   $F7
2138-   A5 F7       LDA   $F7
213A-   C9 10       CMP   #$10
213C-   90 EB       BCC   $2129

; Besides this loop falling through,
; several failure paths end up here,
; because branch instructions can only
; jump $80 bytes in either direction.
; It just continues to the real
; Badlands further down.
213E-   4C B8 21    JMP   $21B8

; execution continues here (from "BEQ"
; at $2130) --
; now look for non-#$DD nibble
2141-   BD 8C C0    LDA   $C08C,X
2144-   10 FB       BPL   $2141
2146-   C5 F9       CMP   $F9    ; #$DD
2148-   D0 0E       BNE   $2158

; and give up after $1000 wrong nibbles
214A-   E6 F2       INC   $F2
214C-   D0 F3       BNE   $2141
214E-   E6 F3       INC   $F3
2150-   A5 F3       LDA   $F3
2152-   C9 10       CMP   #$10
2154-   90 EB       BCC   $2141
2156-   B0 E6       BCS   $213E

; if the first non-#$DD nibble is #$FF,
; that's good
2158-   C5 F8       CMP   $F8    ; #$FF
215A-   F0 06       BEQ   $2162

; otherwise try again (but eventually
; give up after $100 non-#$FF nibbles)
215C-   E6 F5       INC   $F5
215E-   D0 E1       BNE   $2141
2160-   F0 DC       BEQ   $213E

; execution continues here (from "BEQ"
; at $215A) --
; count #$FF nibbles (zero page $F0/1)
; but give up after $1000 of them,
; terminate on #$DD nibble, tolerate up
; to $100 other nibbles (zero page $F4)
2162-   BD 8C C0    LDA   $C08C,X
2165-   10 FB       BPL   $2162
2167-   C5 F8       CMP   $F8    ; #$FF
2169-   F0 0A       BEQ   $2175
216B-   C5 F9       CMP   $F9    ; #$DD
216D-   F0 14       BEQ   $2183
216F-   E6 F4       INC   $F4
2171-   D0 EF       BNE   $2162
2173-   F0 C9       BEQ   $213E
2175-   E6 F0       INC   $F0
2177-   D0 E9       BNE   $2162
2179-   E6 F1       INC   $F1
217B-   A5 F1       LDA   $F1
217D-   C9 10       CMP   #$10
217F-   90 E1       BCC   $2162
2181-   B0 BB       BCS   $213E

; execution continues here (from "BEQ"
; at $216D) --
; if we found too many of either nibble
; then the protection check has failed
2183-   A5 F4       LDA   $F4
2185-   C9 10       CMP   #$10
2187-   B0 B5       BCS   $213E
2189-   A5 F5       LDA   $F5
218B-   C9 10       CMP   #$10
218D-   B0 AF       BCS   $213E

; count of #$FF nibbles needs to be
; between #$0E70 and #$0E90
218F-   A5 F1       LDA   $F1
2191-   C9 0E       CMP   #$0E
2193-   D0 A9       BNE   $213E
2195-   A5 F0       LDA   $F0
2197-   C9 70       CMP   #$70
2199-   90 A3       BCC   $213E
219B-   C9 90       CMP   #$90
219D-   B0 9F       BCS   $213E

; count of #$DD nibbles needs to be
; between #$0570 and #$0590
219F-   A5 F3       LDA   $F3
21A1-   C9 05       CMP   #$05
21A3-   D0 99       BNE   $213E
21A5-   A5 F2       LDA   $F2
21A7-   C9 70       CMP   #$70
21A9-   90 93       BCC   $213E
21AB-   C9 90       CMP   #$90
21AD-   B0 8F       BCS   $213E

; Success! All nibble counts were
; within acceptable ranges. Now turn
; off the drive motor.
21AF-   BD 88 C0    LDA   $C088,X

; restore the code we self-modified
; earlier (at $2057)
21B2-   A9 A9       LDA   #$A9
21B4-   8D 55 20    STA   $2055

; and return to the caller
21B7-   60          RTS

; failure path is here (from $213E) --
; decrement the master counter (set at
; $204B) and try it all again
21B8-   CE 54 20    DEC   $2054
21BB-   D0 12       BNE   $21CF

; if we're tried it all and failed too
; many times, give up --
; beep twice
21BD-   20 DD FB    JSR   $FBDD
21C0-   20 DD FB    JSR   $FBDD

; don't return to the caller
21C3-   68          PLA
21C4-   68          PLA

; and... continue to the game anyway?!?
21C5-   4C 00 40    JMP   $4000

Note what happens here: on success, we
return gracefully to the caller, where
we change 6 seemingly innocuous memory
locations, then jump to $4000 to start
the game:

201B-   A9 4A       LDA   #$4A
201D-   8D 94 5C    STA   $5C94
2020-   A9 0A       LDA   #$0A
2022-   8D 9D 5C    STA   $5C9D
2025-   A9 AA       LDA   #$AA
2027-   8D 20 5D    STA   $5D20
202A-   A9 60       LDA   #$60
202C-   8D 9C 5B    STA   $5B9C
202F-   A9 30       LDA   #$30
2031-   8D DE 5B    STA   $5BDE
2034-   A9 05       LDA   #$05
2036-   8D DF 5B    STA   $5BDF
2039-   4C 00 40    JMP   $4000

On failure, we beep twice (at $21BD),
pop the stack, and jump to $4000 to
start the game anway -- but we never
set those 6 "innocuous" addresses.

How much could that matter, really?

                   ~

               Chapter 3
      In Which It Turns Out That
 In A General Purpose Computing Device
       Every Instruction Matters


Here's the rub: this game is shipped on
disk with intentional bugs. Those six
patches at $201B fix the bugs.

Here, for example, is the surrounding
code around the first two patches, at
$5C94 and $5C9D. (Again, this is part
of the "FACTS" file we BLOADed.)

*5C7EL

5C7E-   98          TYA
5C7F-   48          PHA
5C80-   29 07       AND   #$07
5C82-   0A          ASL
5C83-   0A          ASL
5C84-   09 20       ORA   #$20
5C86-   85 90       STA   $90
5C88-   98          TYA
5C89-   29 38       AND   #$38
5C8B-   0A          ASL
5C8C-   0A          ASL
5C8D-   0A          ASL
5C8E-   0A          ASL
5C8F-   85 8F       STA   $8F
5C91-   98          TYA
5C92-   29 38       AND   #$38
5C94-   EA          NOP           <-- !
5C95-   4A          LSR
5C96-   4A          LSR
5C97-   4A          LSR
5C98-   05 90       ORA   $90
5C9A-   85 90       STA   $90
5C9C-   98          TYA
5C9D-   EA          NOP           <-- !
5C9E-   90 09       BCC   $5CA9
5CA0-   A5 8F       LDA   $8F
5CA2-   09 50       ORA   #$50
5CA4-   85 8F       STA   $8F
5CA6-   4C B2 5C    JMP   $5CB2
5CA9-   0A          ASL
5CAA-   90 06       BCC   $5CB2
5CAC-   A5 8F       LDA   $8F
5CAE-   09 28       ORA   #$28
5CB0-   85 8F       STA   $8F
5CB2-   68          PLA
5CB3-   A8          TAY
5CB4-   68          PLA
5CB5-   AA          TAX
5CB6-   68          PLA
5CB7-   60          RTS

Those patches aren't random; they're
vital instructions in the middle of a
vital subroutine. If the protection
routine fails, those "NOP" instructions
are never fixed ($5C94 never becomes
LSR, $5C9D never becomes ASL), and the
game is unplayable.

Since the game itself patches out its
own protection check the first time it
runs -- putting an "RTS" at $2055 --
the safest patch would be to make that
"RTS" permanent. The protection check
will properly return to the caller, the
intentional bugs will get patched in
memory, and the game will be playable.

T16,S03,$59: A9 -> 60

]PR#6
...works, and it is glorious...

Side B is unprotected.

Quod erat liberand one more thing...

                   ~

               Chapter 4
    I Mean, It's One Bit, Michael.
   What Could It Cost? Ten Dollars?


The sequence of bits on track $22 is
very specific. It was designed to fool
the best bit copiers, and it does. But
why? How does it work? Why can't bit
copiers duplicate it?

This game was published by Adventure
International, better known for its
Scott Adams adventure games, and it
shares the protection bitstream with
those famous adventures. The earliest
sample in my collection is "Graphic
Adventure #1; Adventureland," dated
1982, but the protection may date back
even further.

That's a long time to reuse a copy
protection. Heck, six months was a long
time to reuse a copy protection. This
one is obviously durable.

Here is the original disk, as seen
through the Copy II Plus nibble editor.
(The editor indicates timing bits after
a nibble with inverse. I've displayed
them here as "+" signs.)

                 --v--

   COPY ][ PLUS BIT COPY PROGRAM 8.4
(C) 1982-9 CENTRAL POINT SOFTWARE, INC.
---------------------------------------

TRACK: 22  START: 1800  LENGTH: 3DFF

1CB0: DD+DD+DD+DD+DD+DD+DD+DD+
1CB8: FF 9F E7 F9 FE+FF+FF+FF+
1CC0: FF+FF+FF+FF+FF+FF+FF+FF+
...
2B30: FF+FF+FF+FF+FF+FF+FF+FF+
2B38: FF+FF+FF+FF+FF+FF+E9 BA+
2B40: DD+DD+DD+DD+DD+DD+DD+DD+
...
30B0: DD+DD+DD+DD+DD+DD+DD+DD+
30B8: DD+DD+DD+DD+DD+DD+FF 9F
30C0: E7 F9 FE+FF+FF+FF+FF+FF+

                 --^--

The offsets ($1CB0, $2B30, $30B0) are
not important here; in fact, they will
vary on every read. There's no defined
"start" to a track; it's an unending
circle of bits. The offsets depend on
the physical rotation of the disk when
Copy II Plus started reading. It's the
nibble counts that matter. That's what
the protection check cares about.

  - $DD (repeated $057E times)
  - $FF $9F $E7 $F9 $FE
  - $FF (repeated $0E81 times)
  - $E9 $BA

And, as indicated, there are timing
bits between most nibbles. But that's
it; in this view, the $9F nibble at
offset $1CB9 is the same as the $9F
nibble at offset $30BF. Those are the
same bits. That's the point at which,
pardon the pun, we've literally come
full circle.

Now let's copy the disk with a bit copy
program. I used EDD 4.4 for this test,
one of the most advanced bit copiers
even developed.

                 --v--

TRACK: 22  START: 1800  LENGTH: 3DFF

24C0: FF+FF+FF+FF+FF+FF+FF+FF+
24C8: E9 BA DD DD+DD+DD+DD+DD+
24D0: DD+DD+DD+DD+DD+DD+DD+DD+
...
2A40: DD+DD+DD+DD+DD+DD+DD+DD+
2A48: FF 9F E7 F9 FE FE DB AD
2A50: D6 EB EB+EB+EB+EB+EB+EB+
2A58: EB+EB+EB+EB+EB+EB+EB+EB+
...
2C90: EB+EB+EB+EB+EB+EB+EB+EB+
2C98: FE+FF+FF+FF+FF+FF+FF+FF+
2CA0: FF+FF+FF+FF+FF+FF+FF+FF+
...
3B10: FF+FF+FF+FF+FF+FF+FF+FF+
3B18: FF+FF+FF+FF+FF+E9 BA DD
3B20: DD+DD+DD+DD+DD+DD+DD+DD+

                 --^--

$057E $DD nibbles -- exactly the same
as the original disk. (The acceptable
range was $0570..$0590.) And $0E84 $FF
nibbles -- a few more than the original
disk, but still within acceptable range
($0E70..$0E90).

But what's all that in between? After
the $DD nibbles, we see the sequence
$FF $9F $E7 $F9 $FE, but then it goes
off into the weeds.  $FE $DB $AD $D6,
then a long sequence of... $EB?

Where did $EB come from?

And by the way, how can all these extra
nibbles fit on a track that is supposed
to be the same size as the original
disk?

In retrospect, that last question
points the way to the real answer, but
I could not see it at the time. For me,
the answer did not come from within the
Apple II, because, like so many copy
protection questions, the answer is not
directly visible from within the Apple
II. I had to jump out of the system.

With modern tools, we can now create
bit-perfect images of physical Apple II
floppy disks, then examine those images
on modern systems. Here is what I found
on my bit-perfect image of this 35-year
old floppy disk:

   /--DD--\  /--DD--\  /--DD--\
   110111010011011101001101110100 ...

Over and over and over and over. Those
are the $DD nibbles we saw earlier in
Copy II Plus, the ones that EDD 4 was
able to count and copy successfully...
or did it?

On the original disk, each $DD nibble
is followed by 2 "timing" (0) bits.
Since each valid nibble needs to start
with a "1" bit, those "0" bits between
nibbles are ignored. Even the copy
protection code ignores them. But that
doesn't mean they're useless.

Looking at the failed bit copy (again
from outside the system, via a disk
image that I could only examine on a
modern PC), those $DD nibbles look like
this:

   /--DD--\ /--DD--\ /--DD--\
   110111010110111010110111010 ...

Each $DD nibble is followed by only 1
"timing" (0) bit! They will still be
read as $DD nibbles; in fact, the copy
protection code successfully reads and
counts them. But the underlying bits on
the disk are very different. The
original disk had a 10-bit pattern; the
copy has a 9-bit pattern.

What's going on?

Disks spin independently from the Apple
II's CPU, which is very slow (1 Mhz).
There is barely enough time to read the
bits as they spin by the drive head,
and there is no way to slow down the
disk to read them more accurately. The
drive controller card (the thing in
slot 6) had to "help" by accumulating
the bits into an 8-bit value which
could be read by the main CPU.

Standard floppy disks were designed to
compensate for the limits of the
hardware; protected disks were designed
to exploit them. EDD 4 devised a clever
algorithm to detect the presence or
absence of timing bits after a nibble.
But it couldn't tell *how many* timing
bits are after any particular nibble:
1 or 2. There just wasn't enough time
to tell the difference before the next
bits started coming in.

EDD 4 isn't making a perfect copy of
this disk, because no Apple II program
could make a perfect copy of any disk.
It's making a reconstruction.

EDD 4 knows there is at least 1 timing
bit after each $FF nibble. It decides
to write out 2 timing bits after the
first four $FF nibbles, because $FF is
often used as a "self-synchronizing"
nibble value with the 10-bit pattern
1111111100 repeated four times. (See
p. 3-8 of "Beneath Apple DOS" for a
more detailed explanation of how this
works, and why four 10-bit nibbles is
sufficient to synchronize.) For the
rest of the $FF nibbles, EDD 4 guesses
1 timing bit, creating a 9-bit pattern
111111110. This is wrong, by the way;
the original disk had a 10-bit pattern
1111111100. But in the absence of
perfect information, EDD 4 decides that
it's better to drop bits and have too
few than to add bits and have too many.
If it guessed 2 timing bits where the
original had only 1, too often, it
could end up overwriting the start of
the track.

The original disk also used 2 timing
bits after each $DD nibble. This is an
unusual nibble value, so EDD 4 guesses,
incorrectly, that there is only one
timing bit after every $DD nibble. It
writes the 9-bit repeating pattern
110111010, which is different than the
original disk's 10-bit pattern of
1101110100.

Now EDD 4 has a problem: the track it's
reconstructed is too short, and it
doesn't know why. With the benefit of
hindsight and being able to jump out of
the system, we can see why: EDD 4 is
writing out 9-bit $DD nibbles instead
of 10-bit, and, with few exceptions,
9-bit $FF nibbles instead of 10-bit.

That's $057E + $0E80 bits short!

Then, like it always does, EDD 4 writes
out the track data again, from where it
ended the first time (more or less).
Now our copy has the wrong data twice,
part of which gets overwritten by the
wrong data from the first time and part
of which doesn't.

I believe the technical term for this
is "a complete mess."

Wait, it gets worse.

                   ~

               Chapter 5
     Now You See It, Now You Don't


There's only one thing you can put on a
disk that will change every time you
read it: nothing. And by "nothing," I
mean "a long sequence of zero bits."
And that's what is on the original disk
between each of these long groups of
nibbles: nothing.

A bit of background. When we say a
"zero bit," we really mean "the lack of
a magnetic state change." The Disk II
drive isn't digital; it's analog. If it
doesn't see a state change in a certain
period of time, it calls that a "0". If
it does see a change, it calls that a
"1". But the drive can only tolerate a
lack of state changes for so long --
about as long as it takes for two bits
to go by.

Fun fact(*): this is why you need to
use nibbles as an intermediate on-disk
format in the first place. No valid
nibble contains more than two zero bits
consecutively, when written from most-
significant to least-significant bit.

(*) not guaranteed, actual fun may vary

So what happens when a drive doesn't
see a state change after the equivalent
of two consecutive zero bits? The drive
thinks the disk is weak, and it starts
increasing the amplification to try to
compensate, looking for a valid signal.
But there is no signal. There is no
data. There is just a yawning abyss of
nothingness. Eventually, the drive gets
desperate and amplifies so much that it
starts returning random bits based on
ambient noise from the disk motor and
the magnetism of the Earth.

Seriously.

It's trivial to write zero bits to a
disk; just write a #$00 nibble to write
8 zero bits -- like any other 8-bit
nibble. You can write whatever you want
to a disk; it doesn't need to be what
DOS would consider a "valid" nibble.
But when you read that nibble back, the
drive can't handle 8 zero bits in a
row, so it will actually return some
random bits. Which is why no one does
that.

Returning random bits doesn't sound
very useful for a storage device, but
it's exactly what the developer wanted,
and that's exactly what this copy
protection scheme depends on. Here's
why:

Bit copiers can't duplicate a long
sequence of zero bits.

Why? Because that's not what they see.
What they see is some random bits --
the real zero bits interspersed with
phantom "1" bits. So that's what they
write to the target disk. Whatever
randomness they get when they read the
original disk will essentially get
"frozen" onto the copy.

As far as I can tell, the sequence of
10-bit $FF nibbles is followed by 12
extra zero bits, for a total of 14
after the final $FF nibble. (All others
have 2 zero bits.)

Here's what's on the disk between the
last half of the last $FF nibbles and
the first two $DD nibbles:

F--\              /--DD--\  /--DD--\
11110000000000000011011101001101110100
      ^^^^^^^^^^^^

More than two consecutive zero bits
will form an "abyss" that the floppy
drive will fill with randomness.  Some
of the zero bits in the abyss (shown
here above the "^^^^^^^^^^^" line) will
randomly transform into "1" bits. And
it will be a different set every time
you read the disk.

Seriously.

Here's one of many possible bitstreams
that might come out of the abyss:

F--\       /--E9--\/--BA--\ /--DD--\
11110000000111010011011101001101110100
      ^^^^^^^^^^^^

That's what I saw the first time I read
the original disk with the Copy II Plus
nibble editor: the abyss coalesces into
one full nibble ($E9). But if you look
closely, you'll see that the $E9 nibble
is stealing a bit from the first $DD
nibble. The number of $DD nibbles will
be one fewer this time, because the $E9
nibble swallowed the first one.

Now we're out of phase from the start
of the $DD nibbles, because the $E9
nibble stole a bit. After $E9, we see
a $BA nibble made up of bits 2-8 of
what ought to be the first $DD nibble,
plus one of the timing bits as its
least significant bit. Then we ignore
one timing bit after $BA, and now we're
back in phase, reading 10-bit $DD
nibbles again.

Then I read the original disk again and
got a completely different bitstream
between the last $FF and the first $DD.
It looked like this:

F--\  /--ED--\ /--9B--\/--A6--\/--E9--\
111100111011010100110111010011011101001
      ^^^^^^^^^^^^

/--BA--\ /--DD--\  /--DD--\  /--DD--\
101110100110111010011011101001101110100

Again, the random bits in the abyss are
interpreted as real bits, and they fool
the disk reading code into reading a
long sequence of out-of-phase nibbles,
$ED $9B $A6 $E9 $BA, before we finally
get back in phase and start reading
10-bit $DD nibbles again.

I say "finally" get back in phase, but
it really didn't take that long. No
matter how many bits get swallowed by
the abyss, the 10-bit $DD pattern will
always resynchronize. We'll always get
back in phase in time to find at least
$0570 $DD nibbles.

Now let's consider what happens on our
attempted copy. First of all, there is
no randomness, no abyss. Whatever
random bits it read from the original
disk, it writes those bits to the copy,
followed by the 9-bit $DD nibbles.

F--\ /--ED--\ /--9B--\/--AD--\/--D6--\
11110111011010100110111010110111010110
     ^^^^^^^^^^^^

/--EB--\ /--EB--\ /--EB--\ /--EB--\
111010110111010110111010110111010110

The initial sequence of out-of-phase
nibbles is different, between the
number of timing bits is different.
But we never get back to $DD. In fact,
we quickly reach a steady state where
we're reading out-of-phase $EB nibbles
over and over again.

The 9-bit $DD pattern will never
resynchronize itself. If you ever get
out of phase, you're going to stay out
of phase. Of course, you might not ever
get out of phase; it depends on which
bits come out of the abyss. But that's
where the $EB comes from: it's the
9-bit $DD, shifted by 3 bits.

Wait, it gets worse.

                   ~

               Chapter 6
 In Which Everything Is Designed To Be
   As Bad As Possible, And Succeeds


Track $22 on the original disk is split
up roughly 70/30, like this:

        1111111100         1101110100
|-----------------------|?|----------|?
       (10-bit $FF)       (10-bit $DD)

The first, longer section is full of
10-bit $FF nibbles. The second, shorter
section is full of 10-bit $DD nibbles.
The two "?" areas are the "weak" bits,
the abyss, which appear different every
time you read the disk and consume a
small number of the nibbles that follow
(but only a small number, because both
10-bit $FF and 10-bit $DD nibbles will
resynchronize).

The failed bit copy looks like this:

     111111110        110111010
|------------------|?|---------|?|/////
    (9-bit $FF)      (9-bit $DD)

The "/////" at the end of the track is
not modified (yet), because EDD 4 is
writing 9-bit nibbles instead of 10-bit
nibbles, so the track is shorter than
the original. But then EDD 4 writes the
data again, starting in the "/////"
region and wrapping around to overwrite
most (but not all) of the track it just
wrote:

     111111110        110111010   1101
|------------------|?|---------|?|----|
    (9-bit $FF)      (9-bit $DD)

That last section is detritus, leftover
9-bit $DD nibbles from the previous
write. Of course, these may also end up
out of phase, depending on how the bits
in the abyss come out. And as we've
seen, once a 9-bit $DD nibble gets out
of phase, it stays out of phase.

All of which explains what I initially
saw when I examined my failed bit copy
in the Copy II Plus nibble editor:

                 --v--

TRACK: 22  START: 1800  LENGTH: 3DFF

: FF+FF+FF+FF+FF+FF+FF+FF+
: E9 BA DD DD+DD+DD+DD+DD+ | 9-bit $DD
: DD+DD+DD+DD+DD+DD+DD+DD+ | nibbles
...
: DD+DD+DD+DD+DD+DD+DD+DD+
: FF 9F E7 F9 FE FE DB AD  | abyss
: D6 EB EB+EB+EB+EB+EB+EB+
: EB+EB+EB+EB+EB+EB+EB+EB+ | detritus
...
: EB+EB+EB+EB+EB+EB+EB+EB+
: FE+FF+FF+FF+FF+FF+FF+FF+ | 9-bit $FF
: FF+FF+FF+FF+FF+FF+FF+FF+ | nibbles
...
: FF+FF+FF+FF+FF+FF+FF+FF+
: FF+FF+FF+FF+FF+E9 BA DD  | abyss
: DD+DD+DD+DD+DD+DD+DD+DD+

                 --^--

Astute readers may complain that I've
oversimplified, and they would be
technically correct (the best kind of
correct). There's no guarantee that the
detritus is $DD nibbles; it could be
any part of the track, depending on
where the bit copier decides the track
starts and ends. There's no guarantee
that another bit copier would write the
track data twice. Other bit copiers
might make different guesses about the
number of timing bits. It appears that
one of the choices EDD 4 made -- adding
2 timing bits after the first few $FF
nibbles -- fixed the desynchronization
caused by the random bits after the $DD
nibbles. But this countermeasure was
not enough on its own.

This protection check is very strict.
It expects a track with a range of $DD
nibbles and a range of $FF nibbles and
very little else. To fool it, you'd
need to make a perfect bit copy. To do
that, you'd need to make a perfect bit
copier. No one ever did.

Quod erat liberandum.

---------------------------------------
A 4am crack                    No. 1563
------------------EOF------------------
